package com.unbiz.common.collection;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.keyvalue.MultiKey;
import org.apache.commons.collections4.map.MultiKeyMap;








import com.unbiz.common.MapUtil;
import com.unbiz.common.io.WriterUtil;
import com.unbiz.common.mutable.MutableInteger;
import com.unbiz.tuple.Tuple;
import com.unbiz.tuple.Tuple2;

public class LabeledBag {
	MultiKeyMap<String,MutableInteger> labelsMap = new MultiKeyMap<>();
	
	Map<String,MutableInteger> labels= new LinkedHashMap<>();
	
	
	private Transformer<String,String> preProcessor=null;
	
	
	public Transformer<String, String> getPreProcessor() {
		return preProcessor;
	}

	public void setPreProcessor(Transformer<String, String> preProcessor) {
		this.preProcessor = preProcessor;
	}

	
		
	public int size(){
		return labelsMap.size();
	}
	
	//if new return true
	public boolean label(String label, String key){		
		return label(label,key,1);
	}
	
	
	//if new return true
	public boolean label(String label, String key,int count){
		String doc = key;
		if(preProcessor!=null){
			doc = preProcessor.transform(key);
		}
		if(doc==null || doc.isEmpty()) return false;
		MutableInteger freq = labelsMap.get(doc,label);
		if(freq==null){
			labelsMap.put(doc,label,new MutableInteger(count));
			
			MutableInteger freq2 = labels.get(label);
			if(freq2==null){
				labels.put(label, new MutableInteger(count));
			}
			else{
				freq2.setValue(freq2.getValue()+count);
			}
			return true;
		}
		else{
			freq.setValue(freq.getValue()+count);
		}
		return false;
	}
		

	public String findLabel(String key){		
		double f=0;
		String theLabel ="";
		for(Entry<String,MutableInteger> label: labels.entrySet()){
			MutableInteger freq = labelsMap.get(key,label.getKey());
			double labelFreq = 1.0/label.getValue().intValue();
			if(freq!=null && labelFreq*freq.intValue()>f){
				theLabel = label.getKey();
				f = labelFreq*freq.intValue();
			}
			
		}
		return theLabel;
	}
	
	public boolean containsKey(String label,String key){	
		MutableInteger freq = labelsMap.get(key,label);
		if(freq!=null)
			return freq.intValue()>0;
		return false;
	}
	
	public int getFreq(String label,String key){		
		int f=0;
		MutableInteger freq = labelsMap.get(key,label);
		if(freq!=null)
			return freq.intValue();
		return 0;
	}
	
	public <V> List<Entry<MultiKey<? extends String>, MutableInteger>> sortMapByValue(boolean reserved) {
		String label="",doc="";
		List<Entry<MultiKey<? extends String>, MutableInteger>> entryList = new ArrayList<Entry<MultiKey<? extends String>, MutableInteger>>();
		Set<Entry<MultiKey<? extends String>, MutableInteger>> freq = labelsMap.entrySet();
		for(Entry<MultiKey<? extends String>, MutableInteger> ents: freq){
			MutableInteger i = ents.getValue();
			MultiKey<? extends String> keys = ents.getKey();
			doc = keys.getKey(0);
			label = keys.getKey(1);
			if(!label.isEmpty()){				
				entryList.add(ents);				
			}
		}
		if(reserved){			
			Collections.sort(entryList, MapUtil.defaultReverseComparator);
		}
		else{
			Collections.sort(entryList, MapUtil.defaultComparator);
		}
		return entryList;
	}
	
	/**
	 * 只保存label统计中出现次数最多的item
	 * @param dir
	 * @param trans 参数为keyword
	 * @return
	 */
	public boolean saveKeys(String dir,Transformer<String, String> trans){		
		String label="",doc="";		
		
		LinkedHashMap map = MapUtil.sortMapByValue(this.labels, true);
		MapUtil.save(map, dir+"/_labels.txt");
		
		HashSet<String> saved = new HashSet<>();
		List<Entry<MultiKey<? extends String>, MutableInteger>> items = sortMapByValue(true);	
		//Set<Entry<MultiKey<? extends String>, MutableInteger>> freq = labelsMap.entrySet();
		for(Entry<MultiKey<? extends String>, MutableInteger> ents: items){
			MutableInteger i = ents.getValue();
			MultiKey<? extends String> keys = ents.getKey();
			doc = keys.getKey(0);
			label = keys.getKey(1);
			if(!label.isEmpty() && !saved.contains(doc)){
				saved.add(doc);
				String file = dir+"/"+label+"/"+label+".txt";
				try {
					if(trans!=null){
						doc = trans.transform(doc);
					}
					if(doc!=null && !doc.isEmpty()){
						Writer writer = WriterUtil.getWriter(file,true);
						writer.append(doc+'\t'+i);
						writer.append('\n');
						writer.flush();
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return true;
		
		
	}
	
	/**
	 * 保存所有item和label统计
	 * @param dir
	 * @param trans, 参数为keyword
	 * @return
	 */
	public boolean saveAll(String dir,Transformer<String, String> trans){
		String label="",doc="";
		List<Entry<MultiKey<? extends String>, MutableInteger>> items = sortMapByValue(true);		
		//Set<Entry<MultiKey<? extends String>, MutableInteger>> freq = labelsMap.entrySet();		
		
		LinkedHashMap map = MapUtil.sortMapByValue(this.labels, true);
		MapUtil.save(map, dir+"/_labels.txt");
		
		for(Entry<MultiKey<? extends String>, MutableInteger> ents: items){
			MutableInteger i = ents.getValue();
			MultiKey<? extends String> keys = ents.getKey();
			doc = keys.getKey(0);
			label = keys.getKey(1);
			if(!label.isEmpty()){
				String file = dir+"/"+label+"/"+label+".txt";
				try {
					if(trans!=null){
						doc = trans.transform(doc);
					}
					if(doc!=null && !doc.isEmpty()){
						Writer writer = WriterUtil.getWriter(file,true);
						writer.append(doc+'\t'+i);
						writer.append('\n');
						writer.flush();
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return true;
	}
	
	public boolean load(String dir){
		// search for all files in all folders provided
        List<File> fileList = new ArrayList<>();
        List<String> labels = new ArrayList<>();
        File file = new File(dir);
        if(file.exists()){
            if (!file.isDirectory())
                return false;
            
            File[] files = file.listFiles();
            if (files == null || files.length == 0)
            	return false;


            for (File fileLabel : files) {
                if (!fileLabel.isDirectory())
                    continue;

                if (!labels.contains(fileLabel.getName()))
                    labels.add(fileLabel.getName());

                File[] docs = fileLabel.listFiles();
                if (docs == null || docs.length == 0)
                    continue;

                for (File fileDoc : docs) {
                    if (!fileDoc.isDirectory())
                        fileList.add(fileDoc);
                }
            }
        }
        
        for(File fileToRead : fileList){      
	        String label = fileToRead.getParentFile().getName();
	        try {	           
	            BufferedReader reader = new BufferedReader(new FileReader(fileToRead));	            
	            String line = "";
	            while ((line = reader.readLine()) != null){
	            	line = line.trim();	            	
	            	if(line.length()>4){		
	            		line = line.replaceAll("\\(.*?\\)", "");
	        		}	        		
	        		if(line.length()>4){	
	        			line = line.replaceAll("（.*?）", "");			
	        		}
	        		if(line.length()>4){			
	        			line = line.replaceAll("\\[.*?\\]", "");
	        		}
	        		if(line.length()>4){
	        			line = line.replaceAll("【.*】", "");			
	        		}
	            	if(line.length()>1){  
	            		String[] lines = line.split("\t|\\s");
	            		if(lines.length>1){
	            			int c = 1;
	            			try{
	            				c = Integer.parseInt(lines[1]);	            				
	            			}
	            			catch(Exception e){
	            				//e.printStackTrace();
	            			}
	            			finally{
	            				label(label,lines[0],c);
	            			}
	            		}
	            		else{
	            			label(label,lines[0]);
	            		}
	            	}
	            }
	            reader.close();       
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
        }
        return true;
	}
	
}
